[None][perf] optimize encoder-decoder PyTorch performance - #16706
[None][perf] optimize encoder-decoder PyTorch performance#16706cascade812 wants to merge 17 commits into
Conversation
Signed-off-by: Guiju Zhang <7135567+cascade812@users.noreply.github.com>
Signed-off-by: Guiju Zhang <7135567+cascade812@users.noreply.github.com>
Signed-off-by: Guiju Zhang <7135567+cascade812@users.noreply.github.com>
Signed-off-by: Guiju Zhang <7135567+cascade812@users.noreply.github.com>
Signed-off-by: Guiju Zhang <7135567+cascade812@users.noreply.github.com>
Signed-off-by: Guiju Zhang <7135567+cascade812@users.noreply.github.com>
Signed-off-by: Guiju Zhang <7135567+cascade812@users.noreply.github.com>
Signed-off-by: Guiju Zhang <7135567+cascade812@users.noreply.github.com>
Signed-off-by: Guiju Zhang <7135567+cascade812@users.noreply.github.com>
|
/bot run |
|
PR_Github #63174 [ run ] triggered by Bot. Commit: |
|
PR_Github #63174 [ run ] completed with state
|
Signed-off-by: Guiju Zhang <7135567+cascade812@users.noreply.github.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
tensorrt_llm/_torch/pyexecutor/py_executor.py (2)
5588-5593: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winDo not handle invariant failures as request failures.
These
except Exceptionblocks also catchAssertionError,TypeError, and other programming errors. They then call_finish_failed_encoder_step, which can let the executor continue after an internal invariant failure. Catch only documented recoverable encoder execution errors. Re-raise unexpected errors to the event-loop failure path.As per coding guidelines, “Catch specific exceptions instead of using broad or bare exception handling such as
except:.”Also applies to: 5600-5606, 5632-5637, 5643-5647, 5667-5676
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/_torch/pyexecutor/py_executor.py` around lines 5588 - 5593, Replace the broad exception handlers around the encoder-step submission and completion paths, including _run_encoder_step_unchecked and the referenced ranges, with catches for only the documented recoverable encoder execution exceptions. Preserve request cleanup and _finish_failed_encoder_step only for those exceptions, while allowing AssertionError, TypeError, and other unexpected errors to propagate to the event-loop failure path.Source: Coding guidelines
5421-5441: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winAdd the required function annotations and built-in generic types.
Annotate
_get_ctx_mla_kv_len_cap,_context_attended_kv_len,_cap_context_by_total_kv_len, and_publish_encoder_stepwith complete parameter and return types. Replace newly addedList[...]annotations withlist[...].As per coding guidelines, “Annotate every function” and “prefer built-in generic types and
|.”Also applies to: 5443-5455, 5457-5476, 5574-5580, 5652-5653, 5666-5666, 5679-5680, 5713-5714, 5726-5731
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/_torch/pyexecutor/py_executor.py` around lines 5421 - 5441, Add complete parameter and return annotations to _get_ctx_mla_kv_len_cap, _context_attended_kv_len, _cap_context_by_total_kv_len, and _publish_encoder_step, including the additional functions in the referenced changes. Replace every newly introduced List[...] annotation with the equivalent built-in list[...] form, and use | for unions. Preserve the existing runtime behavior while ensuring every function in this change is annotated.Source: Coding guidelines
tests/unittest/llmapi/test_llm_args.py (1)
1749-1778: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winCover the
encode_onlyvalidation branch.
TorchLlmArgs.validate_encoder_cuda_graph_config()also rejectsencoder_cuda_graph_configwhenencode_only=True. Add thisValidationErrorcase with a valid encoder configuration andencoder_max_batch_size.Test coverage summary:
tests/unittest/llmapi/test_llm_args.pyis listed intests/integration/test_lists/test-db/l0_a10.yml. Coverage is insufficient.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/unittest/llmapi/test_llm_args.py` around lines 1749 - 1778, Extend test_encoder_decoder_cuda_graph_user_interface to cover TorchLlmArgs.validate_encoder_cuda_graph_config when encode_only=True: construct the arguments with a valid encoder_cuda_graph_config and encoder_max_batch_size, assert that ValidationError is raised, and preserve the existing valid configuration and mixed-graph toggle coverage.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@tensorrt_llm/_torch/pyexecutor/py_executor.py`:
- Around line 5588-5593: Replace the broad exception handlers around the
encoder-step submission and completion paths, including
_run_encoder_step_unchecked and the referenced ranges, with catches for only the
documented recoverable encoder execution exceptions. Preserve request cleanup
and _finish_failed_encoder_step only for those exceptions, while allowing
AssertionError, TypeError, and other unexpected errors to propagate to the
event-loop failure path.
- Around line 5421-5441: Add complete parameter and return annotations to
_get_ctx_mla_kv_len_cap, _context_attended_kv_len, _cap_context_by_total_kv_len,
and _publish_encoder_step, including the additional functions in the referenced
changes. Replace every newly introduced List[...] annotation with the equivalent
built-in list[...] form, and use | for unions. Preserve the existing runtime
behavior while ensuring every function in this change is annotated.
In `@tests/unittest/llmapi/test_llm_args.py`:
- Around line 1749-1778: Extend test_encoder_decoder_cuda_graph_user_interface
to cover TorchLlmArgs.validate_encoder_cuda_graph_config when encode_only=True:
construct the arguments with a valid encoder_cuda_graph_config and
encoder_max_batch_size, assert that ValidationError is raised, and preserve the
existing valid configuration and mixed-graph toggle coverage.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 63f5f225-8488-4516-8ef6-6e37736bc974
📒 Files selected for processing (5)
tensorrt_llm/_torch/pyexecutor/model_engine.pytensorrt_llm/_torch/pyexecutor/py_executor.pytensorrt_llm/llmapi/llm_args.pytensorrt_llm/usage/llm_args_golden_manifest.jsontests/unittest/llmapi/test_llm_args.py
🚧 Files skipped from review as they are similar to previous changes (3)
- tensorrt_llm/usage/llm_args_golden_manifest.json
- tensorrt_llm/llmapi/llm_args.py
- tensorrt_llm/_torch/pyexecutor/model_engine.py
Signed-off-by: Guiju Zhang <7135567+cascade812@users.noreply.github.com>
|
/bot run |
|
PR_Github #63565 [ run ] triggered by Bot. Commit: |
|
PR_Github #63565 [ run ] completed with state
|
|
/bot run |
|
PR_Github #63639 [ run ] triggered by Bot. Commit: |
|
PR_Github #63639 [ run ] completed with state
|
Signed-off-by: Guiju Zhang <7135567+cascade812@users.noreply.github.com>
|
/bot run |
|
PR_Github #63824 [ run ] triggered by Bot. Commit: |
|
PR_Github #63824 [ run ] completed with state
|
Signed-off-by: Guiju Zhang <7135567+cascade812@users.noreply.github.com>
|
/bot run |
|
PR_Github #63867 [ run ] triggered by Bot. Commit: |
|
PR_Github #63867 [ run ] completed with state
|
|
/bot run |
|
PR_Github #63967 [ run ] triggered by Bot. Commit: |
pengbowang-nv
left a comment
There was a problem hiding this comment.
Attention part change LGTM
| host_request_types=self.host_request_types[:self.num_seqs], | ||
| ) | ||
|
|
||
| def prepare_encoder_decoder(self, prompt_lens: torch.Tensor, |
There was a problem hiding this comment.
NIT, not blocking: maybe change to something like "prepare_encoder_decoder_from_precomputed_lengths". Current name may suggest general prepare won't work for encoder-decoder models.
Summary
Why
BART continuous-admission serving repeatedly mixes replacement encoder requests with active decoder requests. The existing path launched small encoder batches, rebuilt mixed decoder metadata in Python, and reconstructed sampling/finish metadata on every greedy decode step. It also ran eligible encoder and mixed decoder work eagerly instead of replaying fixed-shape CUDA graphs.
These changes reduce per-iteration CPU launch and device-to-host overhead while keeping decoder generation active during encoder admission. The CUDA-graph capture layout is derived from the encoder runner's effective capture keys so padding and runtime replay use the same buckets.
User interface
For encoder-decoder models,
encoder_cuda_graph_config=EncodeCudaGraphConfig(...)enables encoder-forward CUDA graphs and defines batch-size, total packed-token, and maximum sequence-length buckets.encoder_max_batch_sizeremains the hard encoder capacity and admission limit.enable_encoder_decoder_mixed_cuda_graphcontrols the mixed-batch decoder optimization. It defaults toTruebut becomes effective only when both the encoder and decoder graph configurations produce usable capture shapes. Set it toFalseto retain separate encoder and decoder CUDA graphs while disabling mixed-batch graphs.Performance
BART-large-CNN
This comparison uses the same deterministic natural-length CNN/DailyMail workload on both backends: 1,024 unique validation articles sampled without replacement using seed 0, encoder lengths of 93–1,022 tokens, and no truncation.
PyTorch improves throughput by 12.7–20.9% and reduces mean latency by 11.0–17.2% compared with legacy TensorRT.
More BART workload, latency, distribution, and configuration details
Detailed latency percentiles
Encoder input-length distribution
Lengths include the tokenizer's special tokens.
Configuration
PyTorch configuration:
encoder_max_batch_size=2at concurrency 8 and8at concurrency 32/64[1, 2]at concurrency 8 and[1, 2, 4, 8]at concurrency 32/64[512, 1024]Legacy TensorRT configuration:
Generated outputs were not bit-identical: PyTorch averaged approximately 73.37 output tokens per request, while legacy TensorRT averaged 72.32–72.41. Latency and throughput are end-to-end request measurements and are not normalized to identical output-token counts.
FLAN-T5 Large
This comparison uses
google/flan-t5-largeand a deterministic 1,024-request Super-NaturalInstructions workload derived from the officialallenai/natural-instructionsdefault/testsplit. Inputs longer than 512 tokens and reference outputs longer than 128 tokens are rejected rather than truncated.With encoder and mixed encoder-decoder CUDA graphs enabled, PyTorch improves request throughput by 11.8–57.8% and reduces mean latency by 9.4–36.6% compared with legacy TensorRT.
More T5 workload, latency, output-check, and configuration details
Each result is one clean run of 1,024 requests after an untimed warmup of one concurrency-sized request window. Timing covers closed-loop request submission through receipt of the final output.
Detailed throughput and latency measurements
Output checks
Both APIs used greedy decoding with a maximum of 128 generated tokens. The benchmark normalizes the legacy decoder-start and EOS conventions before counting or hashing outputs.
An eight-request encoder-graph smoke test produced exactly the same greedy token sequences as eager execution. Full-run cross-backend token counts differ by at most 34 tokens (0.36%) because BF16 execution and batching change a small number of near-tie decoding decisions. This is a performance benchmark, not a task-accuracy evaluation.
Workload
Prompts use the following form:
Selection is deterministic with seed 0:
google/flan-t5-largetokenizerThe final workload covers 116 test tasks and 12 task categories. During selection, 7,086 candidate instances were rejected for exceeding 512 input tokens and five were rejected for exceeding 128 reference-output tokens.
Encoder input-length distribution
Reference output-length distribution
Configuration
google/flan-t5-large, TP=1, PP=1PyTorch decoder CUDA graph batch sizes:
PyTorch encoder CUDA graph buckets:
Mixed encoder-decoder CUDA graphs are enabled, with encoder-token buckets derived from the encoder runner's captured keys. At concurrency 64, the encoder batch-eight capture bucket is omitted because the full relative-attention layout set exceeds the 80 GB GPU during capture; encoder admission therefore uses batch-four microbatches.
The legacy TensorRT path reuses one BF16 encoder/decoder engine pair built with maximum batch size 64:
Both legacy engines use BF16 BERT-attention, GPT-attention, and GEMM plugins with input-padding removal enabled. Context FMHA is disabled because the legacy T5 implementation does not support T5 relative attention bias through that path.
Encoder CUDA graph correctness validation
Before the relative-position correction, graph replay reused relative-position bias from capture instead of rebuilding it for replayed sequence lengths. For the same eight greedy requests, that produced 158 tokens, seven EOS stops, and one 128-token length stop, versus the eager reference's 32 tokens, eight EOS stops, and no length stops.
After the fix, the graph-enabled smoke run exactly matches eager execution: 32 generated tokens, eight EOS stops, and no length stops. All three full graph-enabled runs also have the expected 1,021 natural EOS stops and three length stops.
Validation
Dev Engineer Review
encoder_cuda_graph_configandenable_encoder_decoder_mixed_cuda_graph.QA Engineer Review
TorchLlmArgstests for encoder graph configuration validation and mixed-graph settings.l0_h100.yml: adds BART and T5 continuous-admission tests.l0_dgx_h100.yml: adds the two-GPU BART continuous-admission test.l0_l40s.yml: removes the superseded T5 mixed-context test.